import { Component, tween, Tween, v2, Vec2, _decorator } from "cc";
import { DEV } from "cc/env";

const { menu, ccclass, property, disallowMultiple } = _decorator;
@ccclass
@disallowMultiple
@menu("自定义插件/果冻效果")
export default class JellyEffect extends Component {
    @property({ tooltip: DEV && '频率（弹跳次数）' })
    frequency: number = 4;
 
    @property({ tooltip: DEV && '衰退指数' })
    decay: number = 2;
 
    @property({ tooltip: DEV && '下压缩放' })
    pressScale: number = 0.2;
 
    @property({ tooltip: DEV && '效果总时长' })
    totalTime: number = 1;
 
    @property({ tooltip: DEV && '播放间隔' })
    interval: number = 1;
 
    @property({ tooltip: DEV && '自动播放' })
    autoPlay: boolean = false;
 
    /** 原始缩放值 */
    private _originalScale: Vec2 = v2(1,1);
 
    private _tween: any = null;//Tween = null;
 
    protected start() {
        // 记录缩放值
        this._originalScale = v2(this.node.scale.x,this.node.scale.y);
        // 播放
        this.autoPlay && this.play();
    }
 
    /**
     * 播放
     * @param repeatTimes 重复次数
     */
    public play(repeatTimes?: number) {
        // 重复次数
        const times = (repeatTimes != undefined && repeatTimes > 0) ? repeatTimes : 10e8;
        // 时长
        const pressTime = this.totalTime * 0.2;         // 收缩时长
        const scaleBackTime = this.totalTime * 0.15;    // 缩放至原始大小时长
        const bouncingTime = this.totalTime * 0.65;     // 弹动时长
        // 振幅
        const amplitude = this.pressScale / scaleBackTime;
        // 播放
        this._tween = tween(this.node)
            .repeat(times,
                tween()
                    .to(pressTime, { scaleX: this._originalScale.x + this.pressScale, scaleY: this._originalScale.y - this.pressScale }, { easing: 'sineOut' })
                    .to(scaleBackTime, { scaleX: this._originalScale, scaleY: this._originalScale })
                    .to(bouncingTime, {
                        scaleX: {
                            value: this._originalScale,
                            progress: (start: number, end: number, current: number, t: number) => {
                                return end - this._getDifference(amplitude, t);
                            }
                        },
                        scaleY: {
                            value: this._originalScale,
                            progress: (start: number, end: number, current: number, t: number) => {
                                return end + this._getDifference(amplitude, t);
                            }
                        }
                    })
                    .delay(this.interval)
            )
            .start();
    }
 
    /**
     * 停止
     */
    public stop() {
        this._tween && this._tween.stop();
        this.node.setScale(this._originalScale.x,this._originalScale.y);
    }
 
    /**
     * 获取目标时刻弹性幅度
     * @param amplitude 幅度
     * @param time 时间
     */
    private _getDifference(amplitude: number, time: number) {
        // 角速度（ω=2nπ）
        const angularVelocity = this.frequency * Math.PI * 2;
        return amplitude * (Math.sin(time * angularVelocity) / Math.exp(this.decay * time) / angularVelocity);
    }
}